//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//

#include "precomp.h"
#include "devload.h"

//////////////////////////////////////////////////////////////////////////////
// Include the arrays with the port initialization commands
#include "ModuleInitCmds.cpp"
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Init static variables
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// This static meber variable defines the names and capabilities of the
// available module ports.
// Modify this array together with the corresponding define "NUMBER_OF_PORTS"
// to add or remove ports and/or modify the capabilities of existing ports.
//////////////////////////////////////////////////////////////////////////////
const CComManager::PortInfo CComManager::m_PortInfo[NUMBER_OF_PORTS] = {
// ----------------------------------------------------------------------------------------------------------------
//   Dbg-Name                Reg-Name              Internal-Reg-Name             Capabilities
// ----------------------------------------------------------------------------------------------------------------
#ifdef AUXCOM_PORT
  {  TEXT("cmd port"),       TEXT("ComPort"),      TEXT("ComPort"),              PORT_CAPS_CMD                   },
  {  TEXT("data port"),      TEXT("DataPort"),     TEXT("DataPortInternal"),     PORT_CAPS_GPRS | PORT_CAPS_CSD  },
  {  TEXT("aux port"),       NULL,                 TEXT("AuxComPort"),           PORT_CAPS_URC                   },
#ifdef SIEMENS_MAGNETO
  {  TEXT("aux data port"),  TEXT("AuxDataPort"),  TEXT("AuxDataPortInternal"),  PORT_CAPS_GPRS | PORT_CAPS_RSA  },
#endif
#else // AUXCOM_PORT

  {  TEXT("cmd port"),       TEXT("ComPort"),      TEXT("ComPortInternal"),   PORT_CAPS_CMD | PORT_CAPS_URC   },
  {  TEXT("data port"),      TEXT("DataPort"),     TEXT("DataPortInternal"),  PORT_CAPS_GPRS | PORT_CAPS_CSD  },

#endif // AUXCOM_PORT
// ----------------------------------------------------------------------------------------------------------------
};

//////////////////////////////////////////////////////////////////////////////
// Currently the one and only instance of the CComManager
//////////////////////////////////////////////////////////////////////////////

CComManager g_ComManager;

//////////////////////////////////////////////////////////////////////////////

extern RHA_FUNCTION_LIST RHAFunctionList;
extern LPCTSTR g_tszRegKeyIntrinsycRIL;

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CComManager::CComManager() {
COM_PORT_TYPE i;

  InitializeCriticalSection(&(m_RilHandleLock.csLock));
  InitializeCriticalSection(&csKeepOrder);
  m_RilHandleLock.dwCnt = 0;
  m_hBasePort = INVALID_HANDLE_VALUE;
  m_pRilHandle= NULL;

  // Load the port names from the registry
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    InitializeCriticalSection(&(m_PortLock[i].csLock));
    m_PortLock[i].dwCnt = 0;
    m_PortUsage[i] = USAGE_INTERNAL;
    m_dwIoCount[i] = 0;
    m_szPortNameInternal[i][0] = 0;
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, m_PortInfo[i].strRegNameInternal, m_szPortNameInternal[i], MAX_PORT_NAME_LEN)) {
      m_szPortNameInternal[i][0] = 0;
    }
    m_szPortName[i][0] = 0;
    if (IsDataPort(i) && m_PortInfo[i].strRegName) {
      if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, m_PortInfo[i].strRegName, m_szPortName[i], MAX_PORT_NAME_LEN)) {
        m_szPortName[i][0] = 0;
      }
    }
    m_pComDevice[i] = NULL;
  }

  // Find the port assignments
  m_CmdPort = INVALID_PORT;
  m_DataPort = INVALID_PORT;
  m_UrcPort = INVALID_PORT;
  m_AuxDataPort = INVALID_PORT;
  m_RsaPort = INVALID_PORT;
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    if (m_PortInfo[i].dwCaps & PORT_CAPS_CSD) {
      if (m_DataPort == INVALID_PORT) {
        m_DataPort = i;
      }
    }
    if (m_PortInfo[i].dwCaps & PORT_CAPS_CMD) {
      if (m_CmdPort == INVALID_PORT) {
        m_CmdPort = i;
      }
    }
    if (m_PortInfo[i].dwCaps & PORT_CAPS_URC) {
      if (m_UrcPort == INVALID_PORT) {
        m_UrcPort = i;
      }
    }
    if (m_PortInfo[i].dwCaps & PORT_CAPS_GPRS) {
      m_AuxDataPort = i;
    }
    if (m_PortInfo[i].dwCaps & PORT_CAPS_RSA) {
      if (m_RsaPort == INVALID_PORT) {
        m_RsaPort = i;
      }
    }
  }

  if (m_AuxDataPort == m_DataPort) {
    m_AuxDataPort = INVALID_PORT;
  }

  m_bInitFlag = FALSE;
  
  TBD_ASSERT(m_CmdPort != INVALID_PORT);
  TBD_ASSERT(m_DataPort != INVALID_PORT);
  TBD_ASSERT(m_UrcPort != INVALID_PORT);
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CComManager::~CComManager() {
COM_PORT_TYPE i;

  // Close all remaining ports devices
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    DestroyPortDevice(m_pComDevice[i]);
    DeleteCriticalSection(&(m_PortLock[i].csLock));
  }
  Deinit();
  DeleteCriticalSection(&(m_RilHandleLock.csLock));
  DeleteCriticalSection(&csKeepOrder);
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize the class with required information.
//
// Params:
//  pRilHandle: Pointer to the connected CRilHandle instance which is needed
//              internally.
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::Init(CRilHandle* pRilHandle) {
  WAITNOUSAGE(m_RilHandleLock);
  m_pRilHandle= pRilHandle;
  m_bInitFlag = TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Tell the class that the information passed to Init() is no longer valid.
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::Deinit(void) {
  WAITNOUSAGE(m_RilHandleLock);
  m_pRilHandle= NULL;
  m_bInitFlag = FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the port name used by FileOpen() for application usage.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  Pointer to the port's name or NULL if the port doesn't exist.
//
//////////////////////////////////////////////////////////////////////////////
TCHAR* CComManager::GetPortName(COM_PORT_TYPE PortType) {
TCHAR* pRet = NULL;

  if (IsValidPort(PortType) && (IsDataPort(PortType) || IsRsaPort(PortType))) {
    if (_tcslen(m_szPortName[PortType])) {
      pRet = m_szPortName[PortType];
    }
  }
  return pRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the port name used by FileOpen() for internal usage.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  Pointer to the port's name or NULL if the port doesn't exist.
//
//////////////////////////////////////////////////////////////////////////////
TCHAR* CComManager::GetInternalPortName(COM_PORT_TYPE PortType) {
TCHAR* pRet = NULL;

  if (IsValidPort(PortType)) {
    if (_tcslen(m_szPortNameInternal[PortType])) {
      pRet = m_szPortNameInternal[PortType];
    }
  }
  return pRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Try to open the given registry section and find the port device
// corresponding to the given "Index" value. If there is no port device
// created yet it will be created by the function.
//
// Params:
//  szRegKey: The registry section of the given port passed to the open
//            function.
//
// Return:
//  Pointer to the port device corresponding to the given registry key or
//  NULL in case of an Error.
//
//////////////////////////////////////////////////////////////////////////////
CComHandle* CComManager::CreatePortDevice(LPWSTR szRegKey) {
CComHandle*  pRet;
HKEY         hKey;
DWORD        dwValueType;
DWORD        dwDataSize;
DWORD        dwIndex;

  pRet = NULL;

  if (!m_bInitFlag)
  {
      RILRetailTrace((TEXT("RilDrv: RIL Driver is not ready!\n")));  
	  return pRet;
  }
  
  if (szRegKey) {
    hKey = OpenDeviceKey(szRegKey);
    if (hKey) {
      // Fetch the index
      dwDataSize = sizeof(DWORD);
      if (SUCCEEDED(RegQueryValueEx(hKey, L"Index", NULL, &dwValueType, (LPBYTE)&(dwIndex), &dwDataSize))) {
        if (dwValueType == REG_DWORD) {
          if (dwDataSize == sizeof(DWORD)) {
            // Here we're sure that we have a valid port index -> get the corresponding port device
            COM_PORT_TYPE  i;
            DWORD          d;
            for (i = 0, d = 0; i < NUMBER_OF_PORTS; i++) {
              if (IsDataPort(i)) {
                d++;
                if (d >= dwIndex) {
                  RILRetailTrace((TEXT("RilDrv: Creating port device for \"%s\" (%s)\r\n"), GetPortName(i), GetDebugName(i)));
                  pRet = CreatePortDevice(i);
                  break;
                }
              }
            }
          }
        }
      }
      RegCloseKey(hKey);
    }
  }
  if (!pRet) {
    RILRetailTrace((TEXT("RilDrv: No port device found for registry section \"%s\", data port not available!\r\n"), szRegKey));
  }
  return pRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Find the port device corresponding to the given port type. If there is no
// port device created yet it will be created by the function.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  Pointer to the port device corresponding to the given port type NULL in
//  case of an Error.
//  
//////////////////////////////////////////////////////////////////////////////
CComHandle* CComManager::CreatePortDevice(COM_PORT_TYPE PortType) {
  WAITNOUSAGE(m_PortLock[PortType]);
  if (!m_pComDevice[PortType]) {
    m_pComDevice[PortType] = new CComHandle(PortType);
    if (m_pComDevice[PortType]) {
      if (!m_pComDevice[PortType]->Init()) {
        delete m_pComDevice[PortType];
        m_pComDevice[PortType] = NULL;
      }
    }
    if (m_pComDevice[PortType]) {
      RILRetailTrace((TEXT("RilDrv: Port device for \"%s\" has been successfully created\r\n"), GetDebugName(PortType)));
    } else {
      RILRetailTrace((TEXT("RilDrv: Failed to create port device for \"%s\"!\r\n"), GetDebugName(PortType)));
    }
  }
  LockPortDevice(PortType);
  return m_pComDevice[PortType];
}

//////////////////////////////////////////////////////////////////////////////
//
// Close the port device corresponding to the given port type. The device will
// only be destroyed if it is no data port because data ports might still be
// in usage by external applications.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  TRUE if the port device was destroyed, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::DestroyPortDevice(COM_PORT_TYPE PortType) {
  WAITNOUSAGE(m_PortLock[PortType]);
  if (m_pComDevice[PortType]) {
    if (!IsDataPort(PortType)) {
      LockPortDevice(PortType);
      delete m_pComDevice[PortType];
      m_pComDevice[PortType] = NULL;
    } else {
      UnlockPortDevice(PortType);
    }
  }
  return m_pComDevice[PortType] == NULL;
}

//////////////////////////////////////////////////////////////////////////////
//
// Destroy the port device corresponding to the given pointer.
//
// Params:
//  pPortDevice: Pointer to the port device we have to destroy.
//
// Return:
//  TRUE if the port device was destroyed, FALSE if there was no port device
//  corresponding to the given pointer found.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::DestroyPortDevice(CComHandle* pPortDevice) {
COM_PORT_TYPE i;

  if (pPortDevice) {
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      WAITNOUSAGE(m_PortLock[i]);
      if (m_pComDevice[i] == pPortDevice) {
        LockPortDevice(i);
        delete m_pComDevice[i];
        m_pComDevice[i] = NULL;
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Open a port device for usage by RIL driver.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::OpenPortDevice(COM_PORT_TYPE PortType) {
  if (IsValidPort(PortType)) {
    CNTUSAGE(&(m_PortLock[PortType]));
    if (m_pComDevice[PortType]) {
      if (m_pComDevice[PortType]->OpenDownstreamPort()) {
        // Lock the Get() function of the response queue
        g_pRspQ[PortType]->SetReadLock(TRUE);
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Close a port device for further usage by RIL driver.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::ClosePortDevice(COM_PORT_TYPE PortType) {
  if (IsValidPort(PortType)) {
    CNTUSAGE(&(m_PortLock[PortType]));
    if (m_pComDevice[PortType]) {
      // Unlock the Get() function of the response queue
      g_pRspQ[PortType]->SetReadLock(FALSE);
      if (m_pComDevice[PortType]->CloseDownstreamPort()) {
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Close all port devices for further usage by RIL driver.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::CloseAllPortDevices(void) {
COM_PORT_TYPE  i;
BOOL           fRet = FALSE;

  CNTUSAGE(&m_RilHandleLock);
  if (m_pRilHandle) {
    fRet = TRUE;
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      ClosePortDevice(i);
    }
    g_PowerManager.DisableCmdRequeueWait();
  }

  CloseBasePort();

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Open and init the base port in case of non mux mode
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::OpenBasePort(void) {

  CNTUSAGE(&m_RilHandleLock);

  if (m_hBasePort == INVALID_HANDLE_VALUE) {

    m_hBasePort = CreateFile(RIL_BASE_PORT_NAME_NON_MUX, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);    

	if (INVALID_HANDLE_VALUE == m_hBasePort)
	{
		RILRetailTrace((TEXT("RilDrv: Unable to OpenBasePort (Failed)\r\n")));
	}

    DCB dcb; 

    memset(&dcb,0,sizeof(dcb)); // zero struct
    
    // Set communication parameters
    dcb.DCBlength = sizeof(DCB);
    dcb.BaudRate = RIL_FFUART_BAUDRATE;
    dcb.fBinary = TRUE;
    dcb.fParity = TRUE;
    dcb.fOutxCtsFlow = TRUE;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcb.fDsrSensitivity = FALSE;
    dcb.fTXContinueOnXoff = TRUE;
    dcb.fOutX = FALSE;
    dcb.fInX = FALSE;
    dcb.fErrorChar = FALSE;
    dcb.fNull = FALSE;
    dcb.fRtsControl = RTS_CONTROL_ENABLE;
    dcb.fAbortOnError = FALSE;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;

    if (!SetCommState(m_hBasePort, &dcb))
    {
        return FALSE;
    }
  }

  return (m_hBasePort != INVALID_HANDLE_VALUE);
}

//////////////////////////////////////////////////////////////////////////////
//
// Close the opened base port.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::CloseBasePort(void) {
  CNTUSAGE(&m_RilHandleLock);
  if (m_hBasePort != INVALID_HANDLE_VALUE) {
    CloseHandle(m_hBasePort);
    m_hBasePort = INVALID_HANDLE_VALUE;
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the file handle of a given port.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  The file handle of the given port or INVALID_HANDLE_VALUE in case of an
//  error.
//
//////////////////////////////////////////////////////////////////////////////
HANDLE CComManager::GetPortHandle(COM_PORT_TYPE PortType) {

	CNTUSAGE(&m_RilHandleLock);

	if (PortType == RIL_BASE_PORT_TYPE) {

		RILRetailTrace((TEXT("RilDrv: CComManager:: GetPortHandle RIL_BASE_PORT_TYPE\n")));			

		return m_hBasePort;
	}

	if (IsValidPort(PortType))
	{		
		if (m_pComDevice[PortType])
		{
			return m_pComDevice[PortType]->GetPortHandle();
		}
	}

	return INVALID_HANDLE_VALUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Unlock a port device for usage by external applications.
//
// Params:
//  PortType: The port type we're looking for.
//  fRsa    : Flag indicating if port is now used for RSA
//
// Return:
//  TRUE if the port device unlocked, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::UnlockPortDevice(COM_PORT_TYPE PortType, BOOL fRsa) {
  if (!IsValidPort(PortType)) {
    return FALSE;
  }

  CNTUSAGE(&(m_PortLock[PortType]));

  if ((m_PortUsage[PortType] == USAGE_INTERNAL) && m_pComDevice[PortType]) {
    RILRetailTrace((TEXT("RilDrv: \"%s\" has been unlocked for external %s usage\r\n"), GetDebugName(PortType), fRsa ? TEXT("RSA") : TEXT("data")));
    m_PortUsage[PortType] = fRsa ? USAGE_RSA : USAGE_DATA;
    Sleep(100);
  }

  return (fRsa ? (m_PortUsage[PortType] == USAGE_RSA) : (m_PortUsage[PortType] == USAGE_DATA));
}

//////////////////////////////////////////////////////////////////////////////
//
// Lock a port device for exclusive internal usage.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  TRUE if the port device locked, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::LockPortDevice(COM_PORT_TYPE PortType) {
  if (!IsValidPort(PortType)) {
    return FALSE;
  }

  CNTUSAGE(&m_RilHandleLock);
  if (m_pRilHandle) {
    CNTUSAGE(&(m_PortLock[PortType]));
    if ((m_PortUsage[PortType] != USAGE_INTERNAL) && m_pComDevice[PortType]) {
      RILRetailTrace((TEXT("RilDrv: \"%s\" has been locked for exclusive internal usage\r\n"), GetDebugName(PortType)));
      m_PortUsage[PortType] = USAGE_INTERNAL;
      // Release all possibly pending IO functions and response threads
      DWORD    i, dwError;
      COMSTAT  tComStat;
      // Since the Read(), Write() and IOControl() functions of the data port called by any
      // application might block they must not be locked with the critical section to avoid
      // possible deadlocks. Although the check of the g_fApplComLocked flag at the start
      // of each function is locked we might have a race condition after this flag check and
      // the call of this disable function which is not avoidable. So the number of called IO
      // functions is counted with a global counter and we're repeatingly releasing them after
      // an additionial short delay until no more are pending. To avoid a lock of the driver in
      // case of any problem with the port hardware this is done no longer than 10 seconds.
      // The return value is dependant of a successful release of all pending IO functions.
      for (i = 0; i < 100; i++) {
        Sleep(50);
        if (!m_pComDevice[PortType]->VirtClearCommError(&dwError, NULL)) break;
        m_pComDevice[PortType]->VirtClearCommBreak();
        if (!m_pComDevice[PortType]->VirtPurgeComm(PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_TXABORT|PURGE_RXABORT)) break;
        if (!m_pComDevice[PortType]->VirtSetCommMask(0)) break;
        Sleep(50);
        if (!m_pComDevice[PortType]->VirtClearCommError(&dwError, &tComStat)) break;
        // We have to ensure that there are no bytes left in the modules send buffer left which
        // had been possibly blocked by flow control before!
        if (!m_dwIoCount[PortType] && !tComStat.cbInQue) {
          break;
        }
      }
      m_dwIoCount[PortType] = 0;
    }
    Sleep(100);
  }

  return (m_PortUsage[PortType] == USAGE_INTERNAL);
}

//////////////////////////////////////////////////////////////////////////////
//
// Execute ReadFile() calls from external applications.
//
// Params:
//  PortType: The port type of the port to be accessed.
//  ...     : The usual ReadFile parameters.
//
// Return:
//  The number of read bytes.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CComManager::ExternalRead(COM_PORT_TYPE PortType, LPVOID pBuf, DWORD len) {
UINT   cbCopied;
DWORD  dwDownstreamRead = 0;
DWORD  dwRet = 0;

  CNTUSAGE(&(m_PortLock[PortType]));

  if (m_pComDevice[PortType] && (m_PortUsage[PortType] != USAGE_INTERNAL) && (m_PortUsage[PortType] != USAGE_RSA_LEAVING)) {
    if (RHAFunctionList.pfnRHA_IsRadioPwrOn()) {
      if ((len > 0) && pBuf) {
        cbCopied = m_pComDevice[PortType]->ReadFromBackupBuffer(pBuf, len);
        m_dwIoCount[PortType]++;
        if ((len > cbCopied) && !m_pComDevice[PortType]->VirtRead((BYTE*)pBuf + cbCopied, len - cbCopied, dwDownstreamRead)) {
          dwRet = (DWORD)-1;
        } else {
          dwRet = cbCopied + dwDownstreamRead;
          m_pComDevice[PortType]->UpdateComStatData(TRUE, dwRet);
        }
        m_dwIoCount[PortType]--;
      }
    } else {
      // Module has been powered off
      RILRetailTrace((TEXT("RilDrv: ExternalRead(%s) -Module has been powered off externally!\r\n"), GetDebugName(PortType)));
      m_PortUsage[PortType] = USAGE_INTERNAL;
      GetRILDevice()->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
      g_PowerManager.ForcePowerOff();
      SetLastError(ERROR_ACCESS_DENIED);
    }
  } else {
    SetLastError(ERROR_ACCESS_DENIED);
  }

  return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Execute WriteFile() calls from external applications.
//
// Params:
//  PortType: The port type of the port to be accessed.
//  ...     : The usual WriteFile parameters.
//
// Return:
//  The number of written bytes.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CComManager::ExternalWrite(COM_PORT_TYPE PortType, LPCVOID pBuf, DWORD len) {
DWORD  dwRet = 0;

  CNTUSAGE(&(m_PortLock[PortType]));

  if (m_pComDevice[PortType] && (m_PortUsage[PortType] != USAGE_INTERNAL) && (m_PortUsage[PortType] != USAGE_RSA_LEAVING)) {
    if (RHAFunctionList.pfnRHA_IsRadioPwrOn()) {
      m_dwIoCount[PortType]++;
      if (m_pComDevice[PortType]->VirtWrite(pBuf, len, dwRet)) {
        m_pComDevice[PortType]->UpdateComStatData(FALSE, dwRet);
      } else {
        dwRet = (DWORD)-1;
      }
      m_dwIoCount[PortType]--;
    } else {
      // Module has been powered off
      RILRetailTrace((TEXT("RilDrv: ExternalWrite(%s) -Module has been powered off externally!\r\n"), GetDebugName(PortType)));
      m_PortUsage[PortType] = USAGE_INTERNAL;
      GetRILDevice()->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
      g_PowerManager.ForcePowerOff();
      SetLastError(ERROR_ACCESS_DENIED);
    }
  } else {
    SetLastError(ERROR_ACCESS_DENIED);
  }

  return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Execute IoControl() calls from external applications.
//
// Params:
//  PortType: The port type of the port to be accessed.
//  ...     : The usual DeviceIoControl parameters.
//
// Return:
//  TRUE onsuccess, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::ExternalIoControl(COM_PORT_TYPE PortType, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut) {
BOOL   fRet = FALSE;
DWORD  dwOutUsed;

  CNTUSAGE(&(m_PortLock[PortType]));

  if (m_pComDevice[PortType] && (m_PortUsage[PortType] != USAGE_INTERNAL) && (m_PortUsage[PortType] != USAGE_RSA_LEAVING)) {
    if (RHAFunctionList.pfnRHA_IsRadioPwrOn()) {
      m_dwIoCount[PortType]++;
      if (m_pComDevice[PortType]->IOControl(dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, dwOutUsed, (m_PortUsage[PortType] == USAGE_DATA) ? TRUE : FALSE)) {
        // Everything fine, copy used bytes
        if (pdwActualOut) {
          *pdwActualOut = dwOutUsed;
        }
        fRet = TRUE;
      }
      m_dwIoCount[PortType]--;
    } else {
      // Module has been powered off
      RILRetailTrace((TEXT("RilDrv: ExternalIoControl(%s) - Module has been powered off externally!\r\n"), GetDebugName(PortType)));
      GetRILDevice()->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
      m_PortUsage[PortType] = USAGE_INTERNAL;
      g_PowerManager.ForcePowerOff();
      SetLastError(ERROR_ACCESS_DENIED);
    }
  } else {
    SetLastError(ERROR_ACCESS_DENIED);
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the port type of the port to be used for a specific gprs context id.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  The port type or INVALID_PORT if there is no port available.
//
//////////////////////////////////////////////////////////////////////////////
COM_PORT_TYPE CComManager::GetPortFromContextId(DWORD dwContextId) {
COM_PORT_TYPE  i;
DWORD          d;

  if ((dwContextId <= 1) || ADD_GPRS_AVAILABLE) {
    for ((ADD_RSA_AVAILABLE ? (i = 0) : (i = (NUMBER_OF_PORTS - 1))), d = 0; ADD_RSA_AVAILABLE ? (i < NUMBER_OF_PORTS) : (i >= 0); ADD_RSA_AVAILABLE ? i++ : i--) {
      CNTUSAGE(&(m_PortLock[i]));
      if ((m_PortInfo[i].dwCaps & PORT_CAPS_GPRS) && m_pComDevice[i] && m_pComDevice[i]->IsDownstreamPortOpen() && (m_PortUsage[i] != USAGE_RSA)) {
        d++;
        if (d >= dwContextId) {
          RILRetailTrace((TEXT("RilDrv: Found port \"%s\" (%s) for Gprs-Context-Id %d\r\n"), GetPortName(i), GetDebugName(i), dwContextId));
          return i;
        }
      }
    }
  }
  RILRetailTrace((TEXT("RilDrv: No port found for Gprs-Context-Id %d!\r\n"), dwContextId));
  return INVALID_PORT;
}

//////////////////////////////////////////////////////////////////////////////
//
// Send the very first initialization commands to the given port.
//
// Params:
//  PortType: The port to use.
//
// Return:
//  TRUE onsuccess, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::SendPreInitCmds(COM_PORT_TYPE PortType) {
char   szResponse[64];
DWORD  i;
BOOL   fRet = FALSE;

  CNTUSAGE(&m_RilHandleLock);
  if (m_pRilHandle) {
    fRet = TRUE;
    for (i = 0; (i < NUMBER_OF_PREINIT_CMDS) && fRet; i++) {
      if (!m_pRilHandle->ExSendATCmdGetRsp(PortType, (char*)g_szPreInitString[i], szResponse, sizeof(szResponse), 5000, 1)) {
        fRet = FALSE;
      }
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Send the pre-PIN initialization commands to all ports.
//
// Params:
//  dwStartIndex: Index into init arrays we have to start with.
//
// Return:
//  TRUE onsuccess, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::SendInitCmds(DWORD dwStartIndex) {
COM_PORT_TYPE           i;
char                    szInit[1024];
char                    szCmd[MAX_ATCMD_LEN];
char*                   pszStart;
char*                   pszEnd;
DWORD                   dwFktIndex;
DWORD                   dwIndex;
const INITSTRING_DATA*  pInitData;
//const char              szPowerSavingCmd[] = "+CFUN=";
//const char              szPowerSavingTimeoutCmd[] = "^SCFG=\"PowerSaver/Mode9/Timeout\",\"";
//char                    szTemp[1024];
//DWORD                   dwPowerSaving;
//char                    szPowerSavingMode[8];
//DWORD                   dwPowerSavingTimeout;
//WCHAR                   wszPowerSavingTimeout[8];
BOOL                    fRet = TRUE;

  // Step through all port devices
  CNTUSAGE(&m_RilHandleLock);
  for (i = 0; i < NUMBER_OF_AVAILABLE_PORTS; i++) {
    CNTUSAGE(&(m_PortLock[i]));
    if (m_pComDevice[i] && m_pComDevice[i]->IsDownstreamPortOpen()) {
      // Get the port function index
      if ((dwFktIndex = GetPortInitIndex(i)) == INVALID_PORT_FKT) {
        // Invalid port index, step through to next port
        RILRetailTrace((TEXT("RilDrv: Unable to find Pre-Pin-Init Command for port %s because of invalid port caps\r\n"), GetDebugName(i)));
        fRet = FALSE;
        continue;
      }

      // Get the init commands
      pInitData = NULL;
      if (g_rgisdModuleInit[dwFktIndex][g_dwModemType][0].szCmd) {
        pInitData = g_rgisdModuleInit[dwFktIndex][g_dwModemType];
      }
      if (!pInitData) {
        if (g_rgisdModuleInit[dwFktIndex][MODEMTYPE_UNKNOWN][0].szCmd) {
          pInitData = g_rgisdModuleInit[dwFktIndex][MODEMTYPE_UNKNOWN];
        }
        if (!pInitData) {
          // No init commands found, step through to next port
          RILRetailTrace((TEXT("RilDrv: Unable to find Pre-Pin-Init Command for port %s\r\n"), GetDebugName(i)));
          fRet = FALSE;
          continue;
        }
      }

      m_pComDevice[i]->VirtPurgeComm(PURGE_RXCLEAR);

      // Now step through the init commands
      for (dwIndex = dwStartIndex; (dwIndex < NUMBER_OF_PREINIT_ENTRIES) && pInitData[dwIndex].szCmd; dwIndex++) {
        szInit[0]=TEXT('\0');

        if (SENDREGINIT(dwIndex)) {
          TCHAR tszKey[MAX_PATH];

          if (GetPortRegKey(i, tszKey)) {
            TCHAR tszRegInit[MAX_ATCMD_LEN];

            tszRegInit[0]=TEXT('\0');
            GetRegistrySZ(HKEY_LOCAL_MACHINE, tszKey, TEXT("PreInitString"), tszRegInit, MAX_ATCMD_LEN);
            if (tszRegInit[0]) {
              strncatz(szInit, AnsiString(tszRegInit), sizeof(szInit));
            }

            tszRegInit[0]=TEXT('\0');
            GetRegistrySZ(HKEY_LOCAL_MACHINE, tszKey, TEXT("TmpInitString"), tszRegInit, MAX_ATCMD_LEN);
            if (tszRegInit[0]) {
              strncatz(szInit, AnsiString(tszRegInit), sizeof(szInit));

              HKEY hKey;
              long lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, 0, &hKey);
              if (lResult == ERROR_SUCCESS) {
                RegDeleteValue(hKey, TEXT("TmpInitString"));
                RegCloseKey(hKey);
              }
            }
          }
        }

        if (pInitData[dwIndex].szCmd[0]) {
          strncatz(szInit, pInitData[dwIndex].szCmd, sizeof(szInit));
        }

        if (SENDREGINIT(dwIndex)) {


          TCHAR tszKey[MAX_PATH];

          if (GetPortRegKey(i, tszKey)) {
            TCHAR tszRegInit[MAX_ATCMD_LEN];

            tszRegInit[0]=TEXT('\0');
            GetRegistrySZ(HKEY_LOCAL_MACHINE, tszKey, TEXT("ComInitString"), tszRegInit, MAX_ATCMD_LEN);
            if (tszRegInit[0]) {
              strncatz(szInit, AnsiString(tszRegInit), sizeof(szInit));
            }
          }

          
        }

        // Add the power saving setting if set in the registry
        if (i == m_CmdPort) {
          /* 
          if ((dwIndex == COM_INIT_INDEX + 1) || (dwIndex == COM_REINIT_INDEX + 1)) {
             
            strcpy(szTemp, szInit);
            dwPowerSaving = 1;
            GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("EnablePowerSaving"), &dwPowerSaving);
            dwPowerSavingTimeout = 0;
            GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("EnablePowerSavingTimeout"), &dwPowerSavingTimeout);
            if (dwPowerSavingTimeout >= 5 && dwPowerSavingTimeout <= 36000 && dwPowerSaving == 9) {
                char szPowerSavingTimeoutUCS2[42];

                memset(szPowerSavingTimeoutUCS2,0,42);
                _itow(dwPowerSavingTimeout, wszPowerSavingTimeout, 10);
                strcpy(szInit, szPowerSavingTimeoutCmd);
                UCS2ToGSMHex(wszPowerSavingTimeout, wcslen(wszPowerSavingTimeout), szPowerSavingTimeoutUCS2, 42);
                strcat(szInit, szPowerSavingTimeoutUCS2);
                strcat(szInit, "\"|");
                //strcat(szInit, szPowerSavingCmd);
            }
            else {
                //strcpy(szInit, szPowerSavingCmd);
            }
            //_itoa(dwPowerSaving, szPowerSavingMode, 10);
            //strcat(szInit, szPowerSavingMode);
            strcat(szInit, ";");
            strcat(szInit, szTemp);
          }
          */
        }

        // Now go through the string and break it up into individual commands separated by a '|'
        pszStart = szInit;
        while (TRUE) {
          // Look for the end of the current command
          pszEnd = strchr(pszStart, '|');
          if (pszEnd) {
            // If we found a termination char, terminate the command there
            *pszEnd = '\0';
          }

          UINT nRetriesOnError = 0;
          UINT nRetryOnErrorDelay = 0;
          char* pszRetry = strchr(pszStart, '\\');
          if (pszRetry) {
            char *pszOpt = pszRetry++;
            if (! ParseUInt(pszRetry, FALSE, nRetriesOnError, pszRetry)     ||
                ! MatchStringBeginning(pszRetry, ",", pszRetry)             ||
                ! ParseUInt(pszRetry, FALSE, nRetryOnErrorDelay, pszRetry)  ||
                ! MatchStringBeginning(pszRetry, "\\", pszRetry)) {
              nRetriesOnError = 0;
              nRetryOnErrorDelay = 0;
            } else {
              *pszOpt = '\0';
            }
          }

          // Send the command
          nRetriesOnError = max(2, nRetriesOnError);
          (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT%s\r", pszStart);

          
          if (!QueueInternalCmd(i, m_pRilHandle, szCmd, pInitData[dwIndex].dwCmdOption | CMDOPT_IGNORERSP, APIID_NONE, g_TimeoutCmdInit, nRetriesOnError, nRetryOnErrorDelay)) {
            // Couldn't send an init string -- signal critical error
            SignalCriticalError(RILLOG_EVENT_COULDNTSENDINIT);
            fRet = FALSE;
            break;
          }

          // If this was the last command, get out now
          if (!pszEnd) {
            break;
          }

          // Get the next command
          pszStart = pszEnd + 1;
        }
      }

      /* work around for flow control (Jin Tang) */
      //Sleep(2000);
    }
    }

  /*
  Sleep(10000);  
  sprintf(szCmd, "AT&V\r");

  if (!QueueInternalCmd(COMMAND_PORT, m_pRilHandle, szCmd, CMDOPT_INIT | CMDOPT_FINALINIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_NONE, g_TimeoutCmdInit, 0, 0)) {
    RILRetailTrace((TEXT("RilDrv: Error! Not Send AT&V!\n")));
    fRet = FALSE;
  }

  RILRetailTrace((TEXT("RilDrv: Send AT&V Successful!\n")));
  */

  // Finally initialize the correct audio path
  if (fRet) {
    if (g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwInterface != 0 && g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwAudioMode > 1)
    {
        sprintf(szCmd, "AT^SNFS=%d;^SAIC=%d,%d,%d\r", g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwAudioMode, g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwInterface, g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwMicrophone, g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwEarpiece);
    }
    else
    {
        sprintf(szCmd, "AT^SNFS=%d\r", g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwAudioMode ? g_acAudioConfig[GetAudioTableIndex(RIL_AUDIO_HANDSET)].dwAudioMode : 4);
    }


    
    if (!QueueInternalCmd(COMMAND_PORT, m_pRilHandle, szCmd, CMDOPT_INIT | CMDOPT_FINALINIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0)) {
      fRet = FALSE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Send the post-PIN initialization commands to all ports.
//
// Return:
//  TRUE on success, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::SendUnlockedInitCmds(void) {
COM_PORT_TYPE  i;
DWORD          dwFktIndex;
const char*    szInit;
HANDLE         hATReadyEvent[NUMBER_OF_PORTS];
USAGECLASS*    pPortLock[NUMBER_OF_PORTS];
BOOL           fRet = FALSE;

  // First lock all port devices
  CNTUSAGE(&m_RilHandleLock);
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    INCUSAGE(pPortLock[i], &(m_PortLock[i]));
  }

  if (m_pRilHandle) {
    // Send the unlocked init strings
    fRet = TRUE;
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      if (m_pComDevice[i] && m_pComDevice[i]->IsDownstreamPortOpen()) {
        // Get the port function index
        if ((dwFktIndex = GetPortInitIndex(i)) != INVALID_PORT_FKT) {
          // Now get the init string itself
          if (!(szInit = g_szUnlockedInitString[dwFktIndex][g_dwModemType])) {
            szInit = g_szUnlockedInitString[dwFktIndex][MODEMTYPE_UNKNOWN];
          }

          // And finally send the command
          if (szInit) {
            // Create the notification event
            hATReadyEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
            // Place the command into the command queue (this may block if the queue is full)
            if (!QueueInternalCmd(i, m_pRilHandle, szInit, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0, ParseOk, ParseError, (LPVOID)(hATReadyEvent[i]))) {
              RILRetailTrace((TEXT("RilDrv: Unable to queue Post-Pin-Init CCommand for port %s\r\n"), GetDebugName(i)));
              fRet = FALSE;
            }
          } else {
            RILRetailTrace((TEXT("RilDrv: Unable to find Post-Pin-Init Command for port %s\r\n"), GetDebugName(i)));
            fRet = FALSE;
          }
        } else {
          RILRetailTrace((TEXT("RilDrv: Unable to find Post-Pin-Init Command for port %s because of invalid port caps\r\n"), GetDebugName(i)));
          fRet = FALSE;
        }
      }
    }

    // Wait for an OK or ERROR responses to block until commands have finished
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      if (m_pComDevice[i] && m_pComDevice[i]->IsDownstreamPortOpen()) {
        if (hATReadyEvent[i]) {
          if (WaitForSingleObject(hATReadyEvent[i], 10000) != WAIT_OBJECT_0) {
            fRet = FALSE;
          }
          CloseHandle(hATReadyEvent[i]);
        } else {
          fRet = FALSE;
        }
      }
    }
  }

  // Finally unlock all port devices again
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    DECUSAGE(pPortLock[i]);
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the port type for the auxiliary data port if available.
//
// Return:
//  The port type of the auxiliary data port or INVALID_PORT if it is not
//  available.
//
//////////////////////////////////////////////////////////////////////////////
COM_PORT_TYPE CComManager::GetAuxDataPort(void) {
  if (IsValidPort(m_AuxDataPort)) {
    CNTUSAGE(&(m_PortLock[m_AuxDataPort]));
    if (m_pComDevice[m_AuxDataPort] && m_pComDevice[m_AuxDataPort]->IsDownstreamPortOpen() && (m_PortUsage[m_AuxDataPort] == USAGE_INTERNAL)) {
      return m_AuxDataPort;
    }
  }
  return INVALID_PORT;
}

//////////////////////////////////////////////////////////////////////////////
//
// Identify the number of the mux channel to be used for RSA.
//
// Return:
//  The number of the multiplex channel, -1 in case of an error.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CComManager::GetRsaMuxChn(void) {
DWORD dwRet = -1;

  if (ADD_RSA_AVAILABLE) {
    COM_PORT_TYPE RsaPort = GetRsaPort();
    if (IsValidPort(RsaPort)) {
      TCHAR tszKey[MAX_PATH];
      if (GetPortRegKey(RsaPort, tszKey)) {
        DWORD dwDLCI;
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, tszKey, TEXT("DLCI"), &dwDLCI)) {
          dwRet = dwDLCI;
        }
      }
    }
  }
  return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Lock the Remote SIM Access port if it is available for exclusive internal
// usage.
//
// Params:
//  fCallRsaExit: Flag indicating if the Exit() function for the RSA mode
//                shall be called automatically.
//  fFinished   : Flag indicating that the RSA mode has really been left.
//
// Return:
//  TRUE on success, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::LockRsaPort(BOOL fCallRsaExit, BOOL fFinished) {
  if (IsValidPort(m_RsaPort)) {
    CNTUSAGE(&(m_PortLock[m_RsaPort]));
    if ((m_PortUsage[m_RsaPort] == USAGE_RSA) || (m_PortUsage[m_RsaPort] == USAGE_RSA_LEAVING)) {
      CNTUSAGE(&m_RilHandleLock);
      if (m_pRilHandle) {
        m_PortUsage[m_RsaPort] = USAGE_RSA_LEAVING;
        if (fFinished) {
          if (LockPortDevice(m_RsaPort)) {
            m_pRilHandle->UnblockCommandHandling(m_RsaPort);
          }
        }
        if (fCallRsaExit) {
          m_pRilHandle->GetRsa()->Exit(NULL, TRUE);
          m_pRilHandle->GetRsa()->WaitForExit(10000);
        }
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Unlock the Remote SIM Access port if it is available for external RSA
// usage.
//
// Return:
//  TRUE on success, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::UnlockRsaPort(void) {
  if (IsValidPort(m_RsaPort)) {
    CNTUSAGE(&(m_PortLock[m_RsaPort]));
    if (ADD_RSA_AVAILABLE && m_pComDevice[m_RsaPort] && m_pComDevice[m_RsaPort]->IsDownstreamPortOpen()) {
      CNTUSAGE(&m_RilHandleLock);
      if (m_pRilHandle) {
        if (UnlockPortDevice(m_RsaPort, TRUE)) {
          m_pRilHandle->BlockCommandHandling(m_RsaPort);
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Check if the Remote SIM Access port if it is unlocked for external RSA
// usage.
//
// Return:
//  TRUE on success, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::IsRsaPortUnlocked(void) {
  if (IsValidPort(m_RsaPort)) {
    CNTUSAGE(&(m_PortLock[m_RsaPort]));
    if (m_PortUsage[m_RsaPort] == USAGE_RSA) {
      return TRUE;
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the index into the initialization arrays based on the capabilities
// of the given port.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  The port function index or INVALID_PORT_FKT in case of unknown port
//  capabilities.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CComManager::GetPortInitIndex(COM_PORT_TYPE PortType) {
DWORD i;

  for (i = 0; i < NUMBER_OF_PORT_FKTS; i++) {
    if (m_PortInfo[PortType].dwCaps == g_dwPortCaps[i]) {
      return i;
    }
  }
  return INVALID_PORT_FKT;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the registry key corrsponding to the device name of the given port.
//
// Params:
//  PortType: The port type we're looking for.
//  pKey    : Buffer filled with the key name if available (out).
//            The size must be at least MAX_PATH TCHARs!
//
// Return:
//  TRUE onsuccess, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::GetPortRegKey(COM_PORT_TYPE PortType, TCHAR* pKey) {
DWORD  dwLen = MAX_PATH;
DWORD  dwActiveLen = MAX_PATH;
TCHAR  szActiveKey[MAX_PATH];
BOOL   fRet = FALSE;

  if (!GetDeviceKeys(m_szPortNameInternal[PortType], szActiveKey, &dwActiveLen, pKey, &dwLen)) {
    if (dwLen) {
      fRet = TRUE;
    }
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Find port for high priority commands. Preferable executed on the command port,
// but if this one is not free, every port, including URC port, can be used.
//
// Params: CRilHandle *pRilHandle
//
// Return:
//  Free port for command
//
//////////////////////////////////////////////////////////////////////////////
COM_PORT_TYPE  CComManager::GetPriorityPort(CRilHandle *pRilHandle)
{
    COM_PORT_TYPE iPort = COMMAND_PORT;

    // Default: COMMAND_PORT
    // If COMMAND_PORT is busy, try DATA_PORT (if USE_DATA_PORT_FOR_PRIORITY_COMMANDS is set).
    // If DATA_PORT is busy or in data mode, try URC_PORT.
    // If all ports are busy, take COMMAND_PORT although it is not ready yet.
    while (!g_pCmdQ[iPort]->FEmpty() || pRilHandle->FWaitingForRsp(iPort) || pRilHandle->GetComDevice(iPort)->FDataMode())
    {
        if (iPort == COMMAND_PORT)
#ifdef USE_DATA_PORT_FOR_PRIORITY_COMMANDS
            iPort = DATA_PORT;
        else if(iPort == DATA_PORT)     // we could try the aux data port as well on the MC75, but it is more important to keep this port free for GPRS
#endif
            iPort = URC_PORT;
        else
        {
            // everything is blocked, so we use the command port, although it is
            // not free at the moment.
            iPort = COMMAND_PORT;
            break;
        }
    }

	RILRetailTrace((TEXT("CRilHandle::GetPriorityPort iPort = %d\n"), iPort));        
    return iPort;
}

static BOOL IsKeepOrderCommandInQueue(void* pItem, DWORD dwData)
{
    CCommand* pCmd = (CCommand*)pItem;
    BOOL* pFound = (BOOL *)dwData;

    if (pCmd->FKeepOrder())
        *pFound = TRUE;

    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Find port for high priority commands. Preferable executed on the command port,
// but if this one is not free, every port, including URC port, can be used.
// In difference to GetPriorityPort() this function will try to ensure the order of
// commands, which have the CMDOPT_KEEPORDER flag set.
//
// Params: CRilHandle *pRilHandle
//
// Return:
//  Free port for command
//
//////////////////////////////////////////////////////////////////////////////
COM_PORT_TYPE  CComManager::GetPriorityPortKeepOrder(CRilHandle *pRilHandle)
{
    SYNCBLOCK(csKeepOrder);
    COM_PORT_TYPE iPort = COMMAND_PORT;
    CCommand* pCmd;
    BOOL bFound;

    // First: is there a 'keep order' command running on any channel? If so, use this channel.
    while (TRUE)
    {
        // Do we currently execute a 'keep order' command?
        pCmd = pRilHandle->GetCurrentCommand(iPort);
        if (pCmd != NULL)
        {
            if (pCmd->FKeepOrder())
                return iPort;
        }
        // Is there a 'keep order' command waiting in the queue?
        bFound = FALSE;
        g_pCmdQ[iPort]->Enum(IsKeepOrderCommandInQueue, (DWORD)&bFound, FALSE);
        if (bFound)
            return iPort;

        if ((iPort == COMMAND_PORT) && (iPort != DATA_PORT)) 
#ifdef USE_DATA_PORT_FOR_PRIORITY_COMMANDS
            iPort = DATA_PORT;
        else if(iPort == DATA_PORT)     // we could try the aux data port as well on the MC75, but it is more important to keep this port free for GPRS
#endif
        {
            iPort = URC_PORT;

            if ((COMMAND_PORT == iPort) || (DATA_PORT == iPort))
            {
                /* EXIT */
                break;
            }
        }
        else
        {
            // There is currently no 'keep order' command active. So we look for the next
            // free port, starting with the COMMAND_PORT.
            iPort = COMMAND_PORT;
            break;
        }
    }

    // Default: COMMAND_PORT
    // If COMMAND_PORT is busy, try DATA_PORT (if USE_DATA_PORT_FOR_PRIORITY_COMMANDS is set).
    // If DATA_PORT is busy or in data mode, try URC_PORT.
    // If all ports are busy, take COMMAND_PORT although it is not ready yet.
     while (!g_pCmdQ[iPort]->FEmpty() || pRilHandle->FWaitingForRsp(iPort) || pRilHandle->GetComDevice(iPort)->FDataMode())
     {
        if ((iPort == COMMAND_PORT) && (iPort != DATA_PORT))
#ifdef USE_DATA_PORT_FOR_PRIORITY_COMMANDS
            iPort = DATA_PORT;
        else if(iPort == DATA_PORT)     // we could try the aux data port as well on the MC75, but it is more important to keep this port free for GPRS
#endif
        {
            iPort = URC_PORT;

            if ((COMMAND_PORT == iPort) || (DATA_PORT == iPort))
            {
                /* EXIT */
                break;
            }
        }
        else
        {
            // everything is blocked, so we use the command port, although it is
            // not free at the moment.
            iPort = COMMAND_PORT;
            break;
        }
    }

    RILRetailTrace((TEXT("CRilHandle::GetPriorityPortKeepOrder iPort = %d\n"), iPort));        
    return iPort;
}
